#include "TCPServer.h"
#include "Acceptor.h"
#include "TCPConnection.h"
#include "sock.h"
#include "eventloop/Eventloop.h"
#include "eventloop/EventloopThreadPool.h"
#include "log/Logger.h"
#include "utility/assertion.h"
#include "utility/feature.h"


#include <cstring>

TCPServer::TCPServer(Eventloop &loop, const Sockaddr &addr, const std::string &name, bool reusePort):
    _connections{},
    _IPport{addr.IP_port()},
    _name{name},
    _connectionCallback{},
    _initFunc{default_eventloop_thread_init_func},
    _messageCallback{},
    _pool{std::make_shared<EventloopThreadPool>(loop, name)},
    _loop{loop},
    _acceptor{std::make_unique<Acceptor>(loop, addr, reusePort)},
    _connectionID{0},
    _started{false} {
    _acceptor->set_new_connection_callback(
        [this](std::unique_ptr<Socket> &&socket, const Sockaddr &peer) {
            _new_connection_callback(std::move(socket), peer);
        });
}

TCPServer::~TCPServer() {
    LOG_DEBUG << "TCPServer [" << _name << "] destructing" 
              << _connections.size();
    _loop.ASSERT_IN_THIS_THREAD();
    stop();
}

void TCPServer::set_thread_count(std::size_t count) const {
    _pool->set_thread_count(count);
}

void TCPServer::start() {
    if (_started) {
        return;
    }
    _started = true;
    _pool->init_start(_initFunc);
    assert(!_acceptor->is_listening());
    _loop.run_in_loop([this](){ _acceptor->listen(); });
}

void TCPServer::stop() {
    if (!_started) {
        LOG_DEBUG << _name << "not started";
        return;
    }
    for (auto &[name, connection] : _connections) {
        connection->loop().run_in_loop(
            [connection](){ connection->destroy(); });
    }
    _loop.quit();
    _pool->stop();
    _started = false;
}

void TCPServer::_close_callback(const TCPConnectionPtr &connection) {
    _loop.run_in_loop([this, connection](){
        _loop.ASSERT_IN_THIS_THREAD();
        LOG_INFO << "TCPServer [" << _name << "] close connection"
                 << connection->name();
        MAYBE_UNUSED const auto n = _connections.erase(connection->name());
        assert(n == 1);
        connection->loop().queue_in_loop(
            [connection](){ connection->destroy(); }
        );
    });
}

void TCPServer::_new_connection_callback(std::unique_ptr<Socket> &&socket, const Sockaddr &peer) {
    _loop.ASSERT_IN_THIS_THREAD();
    auto &loopInPool = _pool->next_loop();
    const auto connectionName = _name + '-' + _IPport + '#' + std::to_string(++_connectionID);
    LOG_INFO << "TCPServer [" << _name << "]-new connection[" << connectionName 
             << "] from " << peer.IP_port();
    const Sockaddr local{Sock::local_addr(socket->fd())};
    const auto connection = std::make_shared<TCPConnection>(
                            loopInPool, connectionName, std::move(socket), local, peer);
    _connections.insert({connectionName, connection});
    connection->set_connection_callback(_connectionCallback);
    connection->set_close_callback(
        [this](const TCPConnectionPtr &connectionPtr){ 
            _close_callback(connectionPtr);
        });
    connection->set_message_callback(_messageCallback);
    loopInPool.run_in_loop([connection](){ connection->established(); });
}




